Skip to content

Add declarations to make construction of diagnostics in macros easier #2019

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 7, 2023

Conversation

ahoppen
Copy link
Member

@ahoppen ahoppen commented Aug 7, 2023

Essentially, this makes three improvement:

  1. Add types to allow the creation of error/warning/Fix-It messages from string literals. These types have a static diagnostic ID but I think that should be sufficient for most macros.
  2. Add a convenience initializer to Diagnostic to create it with a single Fix-It, eliminating the need to create an array literal containing all Fix-Its
  3. Add a static method on FixIt to create a Fix-It with a single replace change.

This simplifies the creation of a simple diagnostic from

let diagnosticDomain = "AddCompletionHandlerMacro"

struct CanOnlyBeAppliedDoAsyncFunctionErrorMessage: DiagnosticMessage {
  let message: String
  var diagnosticID: SwiftDiagnostics.MessageID { .init(domain: diagnosticDomain, id: "\(Self.self)") }
  var severity: SwiftDiagnostics.DiagnosticSeverity { .error }

  init(_ funcDecl: FunctionDeclSyntax) {
    message = "can only add a \(funcDecl) completion-handler variant to an 'async' function"
  }
}

enum AddCompletionHandlerFixItMessage: FixItMessage {
  case addAsync

  var fixItID: SwiftDiagnostics.MessageID { .init(domain: diagnosticDomain, id: "\(Self.self)") }

  var message: String {
    switch self {
    case .addAsync: return "add 'async'"
    }
  }
}

let diagnostic = Diagnostic(
  node: Syntax(funcDecl.funcKeyword),
  message: CanOnlyBeAppliedDoAsyncFunctionErrorMessage(funcDecl),
  fixIt: FixIt(
    message: AddCompletionHandlerFixItMessage.addAsync,
    changes: [
      .replace(oldNode: Syntax(funcDecl.signature), newNode: Syntax(newSignature))
    ]
  )
)

to

let diagnostic = Diagnostic(
  node: funcDecl.funcKeyword,
  message: MacroExpansionErrorMessage("can only add a \(node) completion-handler variant to an 'async' function"),
  fixIt: FixIt.replace(
    message: MacroExpansionFixItMessage("add 'async'")
    oldNode: funcDecl.signature,
    newNode: newSignature
  )
)

Essentially, this makes three improvement:
1. Add types to allow the creation of error/warning/Fix-It messages from string literals. These types have a static diagnostic ID but I think that should be sufficient for most macros.
2. Add a convenience initializer to `Diagnostic` to create it with a single Fix-It, eliminating the need to create an array literal containing all Fix-Its
3. Add a static method on `FixIt` to create a Fix-It with a single `replace` change.

This simplifies the creation of a simple diagnostic from

```swift
let diagnosticDomain = "AddCompletionHandlerMacro"

struct CanOnlyBeAppliedDoAsyncFunctionErrorMessage: DiagnosticMessage {
  let message: String
  var diagnosticID: SwiftDiagnostics.MessageID { .init(domain: diagnosticDomain, id: "\(Self.self)") }
  var severity: SwiftDiagnostics.DiagnosticSeverity { .error }

  init(_ funcDecl: FunctionDeclSyntax) {
    message = "can only add a \(funcDecl) completion-handler variant to an 'async' function"
  }
}

enum AddCompletionHandlerFixItMessage: FixItMessage {
  case addAsync

  var fixItID: SwiftDiagnostics.MessageID { .init(domain: diagnosticDomain, id: "\(Self.self)") }

  var message: String {
    switch self {
    case .addAsync: return "add 'async'"
    }
  }
}

let diagnostic = Diagnostic(
  node: Syntax(funcDecl.funcKeyword),
  message: CanOnlyBeAppliedDoAsyncFunctionErrorMessage(funcDecl),
  fixIt: FixIt(
    message: AddCompletionHandlerFixItMessage.addAsync,
    changes: [
      .replace(oldNode: Syntax(funcDecl.signature), newNode: Syntax(newSignature))
    ]
  )
)
```

to

```swift
let diagnostic = Diagnostic(
  node: funcDecl.funcKeyword,
  message: MacroExpansionErrorMessage("can only add a \(node) completion-handler variant to an 'async' function"),
  fixIt: FixIt.replace(
    message: MacroExpansionFixItMessage("add 'async'")
    oldNode: funcDecl.signature,
    newNode: newSignature
  )
)
```
@ahoppen ahoppen requested a review from bnbarham August 7, 2023 04:53
@ahoppen
Copy link
Member Author

ahoppen commented Aug 7, 2023

@swift-ci Please test

@ahoppen ahoppen merged commit e9c5d1a into swiftlang:main Aug 7, 2023
@ahoppen ahoppen deleted the ahoppen/simpler-diag-generation branch August 7, 2023 20:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants